// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef V8_OBJECTS_ALLOCATION_SITE_H_
#define V8_OBJECTS_ALLOCATION_SITE_H_

#include "src/objects.h"
#include "src/objects/struct.h"

// Has to be the last include (doesn't have include guards):
#include "src/objects/object-macros.h"

namespace v8 {
namespace internal {

    enum InstanceType : uint16_t;

    class AllocationSite : public Struct {
    public:
        NEVER_READ_ONLY_SPACE
        static const uint32_t kMaximumArrayBytesToPretransition = 8 * 1024;
        static const double kPretenureRatio;
        static const int kPretenureMinimumCreated = 100;

        // Values for pretenure decision field.
        enum PretenureDecision {
            kUndecided = 0,
            kDontTenure = 1,
            kMaybeTenure = 2,
            kTenure = 3,
            kZombie = 4,
            kLastPretenureDecisionValue = kZombie
        };

        const char* PretenureDecisionName(PretenureDecision decision);

        // Contains either a Smi-encoded bitfield or a boilerplate. If it's a Smi the
        // AllocationSite is for a constructed Array.
        DECL_ACCESSORS(transition_info_or_boilerplate, Object)
        DECL_ACCESSORS(boilerplate, JSObject)
        DECL_INT_ACCESSORS(transition_info)

        // nested_site threads a list of sites that represent nested literals
        // walked in a particular order. So [[1, 2], 1, 2] will have one
        // nested_site, but [[1, 2], 3, [4]] will have a list of two.
        DECL_ACCESSORS(nested_site, Object)

        // Bitfield containing pretenuring information.
        DECL_INT32_ACCESSORS(pretenure_data)

        DECL_INT32_ACCESSORS(pretenure_create_count)
        DECL_ACCESSORS(dependent_code, DependentCode)

        // heap->allocation_site_list() points to the last AllocationSite which form
        // a linked list through the weak_next property. The GC might remove elements
        // from the list by updateing weak_next.
        DECL_ACCESSORS(weak_next, Object)

        inline void Initialize();

        // Checks if the allocation site contain weak_next field;
        inline bool HasWeakNext() const;

        // This method is expensive, it should only be called for reporting.
        bool IsNested();

        // transition_info bitfields, for constructed array transition info.
        class ElementsKindBits : public BitField<ElementsKind, 0, 5> {
        };
        class DoNotInlineBit : public BitField<bool, 5, 1> {
        };
        // Unused bits 6-30.

        // Bitfields for pretenure_data
        class MementoFoundCountBits : public BitField<int, 0, 26> {
        };
        class PretenureDecisionBits : public BitField<PretenureDecision, 26, 3> {
        };
        class DeoptDependentCodeBit : public BitField<bool, 29, 1> {
        };
        STATIC_ASSERT(PretenureDecisionBits::kMax >= kLastPretenureDecisionValue);

        // Increments the mementos found counter and returns true when the first
        // memento was found for a given allocation site.
        inline bool IncrementMementoFoundCount(int increment = 1);

        inline void IncrementMementoCreateCount();

        AllocationType GetAllocationType() const;

        void ResetPretenureDecision();

        inline PretenureDecision pretenure_decision() const;
        inline void set_pretenure_decision(PretenureDecision decision);

        inline bool deopt_dependent_code() const;
        inline void set_deopt_dependent_code(bool deopt);

        inline int memento_found_count() const;
        inline void set_memento_found_count(int count);

        inline int memento_create_count() const;
        inline void set_memento_create_count(int count);

        // The pretenuring decision is made during gc, and the zombie state allows
        // us to recognize when an allocation site is just being kept alive because
        // a later traversal of new space may discover AllocationMementos that point
        // to this AllocationSite.
        inline bool IsZombie() const;

        inline bool IsMaybeTenure() const;

        inline void MarkZombie();

        inline bool MakePretenureDecision(PretenureDecision current_decision,
            double ratio, bool maximum_size_scavenge);

        inline bool DigestPretenuringFeedback(bool maximum_size_scavenge);

        inline ElementsKind GetElementsKind() const;
        inline void SetElementsKind(ElementsKind kind);

        inline bool CanInlineCall() const;
        inline void SetDoNotInlineCall();

        inline bool PointsToLiteral() const;

        template <AllocationSiteUpdateMode update_or_check = AllocationSiteUpdateMode::kUpdate>
        static bool DigestTransitionFeedback(Handle<AllocationSite> site,
            ElementsKind to_kind);

        DECL_PRINTER(AllocationSite)
        DECL_VERIFIER(AllocationSite)

        DECL_CAST(AllocationSite)
        static inline bool ShouldTrack(ElementsKind boilerplate_elements_kind);
        static bool ShouldTrack(ElementsKind from, ElementsKind to);
        static inline bool CanTrack(InstanceType type);

// Layout description.
// AllocationSite has to start with TransitionInfoOrboilerPlateOffset
// and end with WeakNext field.
#define ALLOCATION_SITE_FIELDS(V)                       \
    V(kStartOffset, 0)                                  \
    V(kTransitionInfoOrBoilerplateOffset, kTaggedSize)  \
    V(kNestedSiteOffset, kTaggedSize)                   \
    V(kDependentCodeOffset, kTaggedSize)                \
    V(kCommonPointerFieldEndOffset, 0)                  \
    V(kPretenureDataOffset, kInt32Size)                 \
    V(kPretenureCreateCountOffset, kInt32Size)          \
    /* Size of AllocationSite without WeakNext field */ \
    V(kSizeWithoutWeakNext, 0)                          \
    V(kWeakNextOffset, kTaggedSize)                     \
    /* Size of AllocationSite with WeakNext field */    \
    V(kSizeWithWeakNext, 0)

        DEFINE_FIELD_OFFSET_CONSTANTS(HeapObject::kHeaderSize, ALLOCATION_SITE_FIELDS)
#undef ALLOCATION_SITE_FIELDS

        class BodyDescriptor;

    private:
        inline bool PretenuringDecisionMade() const;

        OBJECT_CONSTRUCTORS(AllocationSite, Struct);
    };

    class AllocationMemento : public Struct {
    public:
// Layout description.
#define ALLOCATION_MEMENTO_FIELDS(V)      \
    V(kAllocationSiteOffset, kTaggedSize) \
    V(kSize, 0)

        DEFINE_FIELD_OFFSET_CONSTANTS(HeapObject::kHeaderSize,
            ALLOCATION_MEMENTO_FIELDS)
#undef ALLOCATION_MEMENTO_FIELDS

        DECL_ACCESSORS(allocation_site, Object)

        inline bool IsValid() const;
        inline AllocationSite GetAllocationSite() const;
        inline Address GetAllocationSiteUnchecked() const;

        DECL_PRINTER(AllocationMemento)
        DECL_VERIFIER(AllocationMemento)

        DECL_CAST(AllocationMemento)

        OBJECT_CONSTRUCTORS(AllocationMemento, Struct);
    };

} // namespace internal
} // namespace v8

#include "src/objects/object-macros-undef.h"

#endif // V8_OBJECTS_ALLOCATION_SITE_H_
